#include "common.h"
#include <sys/epoll.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
char *ip;
char *port;
struct MagicPck *pck_cache;
int cache_count = 0;
int p2p_current_state = PEER_CONNEC_PEER_1;
int main()
{
    ip = getenv("clientip");
    port = getenv("clientport");
    // ip = CLIENT;
    // port = CLIENT_PORT;
    SLOG("udp sock on %s:%s", ip, port);
    getchar();
    int s = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
    if (s == -1)
    {
        goto err;
    }
    struct sockaddr_in local = {
        .sin_family = AF_INET,
        .sin_port = htons(atoi(port)),
        .sin_addr = inet_addr(ip)};
    if (bind(s, (struct sockaddr *)&local, sizeof(struct sockaddr_in)) < 0)
    {
        goto err;
    };
    SET_NONBLOCK(s);
    pck_cache = malloc(sizeof(struct MagicPck));
    start(s);
err:
    ELOG("%s", strerror(errno));
}
void start(int fd)
{
    safe_send_to(fd, NULL);
}
struct MagicPck *safe_recv_from(int fd)
{
    int m_siz = sizeof(struct MagicPck);
    struct MagicPck *m_ptr = malloc(m_siz);
    if (m_ptr == NULL)
    {
        goto err;
    }
    int s_siz = sizeof(struct sockaddr);
    struct sockaddr *s_ptr = malloc(s_siz);
    if (s_ptr == NULL)
    {
        free(m_ptr);
        goto err;
    }

    int e = recvfrom(fd, m_ptr, m_siz, 0, s_ptr, &s_siz);
    if (e == -1)
    {
        free(m_ptr);
        free(s_ptr);
        goto err;
    }
    SLOG("recv one pck");
    struct sockaddr_in server = {
        .sin_family = AF_INET,
        .sin_port = htons(atoi(SERVER_PORT)),
        .sin_addr = inet_addr(SERVER),
    };
    if (memcmp(&server, s_ptr, sizeof(struct sockaddr)) == 0)
    {
        free(s_ptr);
        return m_ptr;
    }
    free(m_ptr);
    free(s_ptr);
    return NULL;
err:
    ELOG("%s", strerror(errno));
    return NULL;
}

struct MagicPck *gen_magic_pck(int fd)
{
    struct MagicPck *pck = safe_recv_from(fd);
    if (pck == NULL)
    {
        goto err;
    }
    if (pck->magic == SERVER_SEND)
    {
        log_magic_pck(pck);
        return pck;
    }
    else
    {
        free(pck);
        return NULL;
    }
err:
    ELOG("%s", strerror(errno));
    return NULL;
}
struct sockaddr *gen_peer(const struct MagicPck *pck)
{
    if (pck == NULL)
    {
        return NULL;
    }
    struct sockaddr *addr = malloc(sizeof(struct sockaddr));
    if (addr == NULL)
    {
        goto err;
    }

    memcpy(addr, &(pck->addr), sizeof(struct sockaddr));
    return addr;

err:
    ELOG("%s", strerror(errno));
    return NULL;
}
int safe_send_to(int fd, const struct sockaddr *t)
{

    while (cache_count != 1)
    {
        usleep(300000);
        struct sockaddr_in server = {
            .sin_family = AF_INET,
            .sin_port = htons(atoi(SERVER_PORT)),
            .sin_addr = inet_addr(SERVER),
        };
        struct sockaddr_in local = {
            .sin_family = AF_INET,
            .sin_port = htons(atoi(port)),
            .sin_addr = inet_addr(ip),
        };
        struct MagicPck mypck = {
            .magic = PEER_SEND,
        };
        memcpy(&(mypck.addr), &local, sizeof(struct sockaddr_in));
        sendto(fd, &mypck, sizeof(mypck), 0, (struct sockaddr *)&server, sizeof(server));
        struct MagicPck *pck = gen_magic_pck(fd);
        if (pck != NULL)
        {
            log_magic_pck(pck);
            memcpy(pck_cache, pck, sizeof(struct MagicPck));
            cache_count++;
        }
    }
    ADDRLOG(pck_cache, "peer addr is ");
    int p2p_success_count = 0;
    while (1)
    {

        usleep(500000);

        struct sockaddr *peer = gen_peer(pck_cache);
        if (peer == NULL)
        {
            continue;
        }

        struct MagicPck pck = {
            .magic = p2p_current_state,
            .addr = *peer,
        };

        int addrlen = sizeof(struct sockaddr);
        ADDRLOG(peer, "send to ");
        sendto(fd, &pck, sizeof(struct MagicPck), 0, peer, addrlen);
        if (p2p_current_state == PEER_CONNEC_PEER_3)
        {
            goto chat;
        }
        struct sockaddr addr_recv;
        struct MagicPck pck_recv;
        int err = recvfrom(fd, &pck_recv, sizeof(struct MagicPck), 0, &addr_recv, &addrlen);
        if (err == -1)
        {
            free(peer);
            continue;
        }
        ADDRLOG(&addr_recv, "recv from ");
        if (memcmp(&addr_recv, peer, addrlen) != 0)
        {
            free(peer);
            continue;
        }
        if (pck_recv.magic == PEER_CONNEC_PEER_1)
        {
            p2p_current_state = PEER_CONNEC_PEER_2;
            free(peer);
            continue;
        }
        if (pck_recv.magic == PEER_CONNEC_PEER_2 || pck_recv.magic == PEER_CONNEC_PEER_3)
        {
            p2p_current_state = PEER_CONNEC_PEER_3;
            free(peer);
            continue;
        }

        free(peer);
    }
chat:
    SLOG("start chat");
    pid_t pid = fork();
    printf("fork pid %d",pid);
    if (pid == 0)
    {
        while (1)
        {
            char buf[5] = {0};
            int count = read(0, buf, 5);
            if (count == -1)
            {
                continue;
            }
            struct sockaddr *peer = gen_peer(pck_cache);
            struct MagicPck pck = {
                .magic = PEER_CONNEC_PEER_CHAT,
            };
            memcpy(&pck.addr, buf, count);
            sendto(fd, &pck, sizeof(struct MagicPck), 0, peer, sizeof(struct sockaddr));
            free(peer);
        }
    }
    else if (pid > 0)
    {
        while (1)
        {
            usleep(500000);
            struct sockaddr *peer = gen_peer(pck_cache);
            if (peer == NULL)
            {
                continue;
            }

            struct sockaddr addr_recv;
            int addr_len = sizeof(struct sockaddr);
            struct MagicPck pck_recv;
            int pck_len = sizeof(struct MagicPck);
            int err = recvfrom(fd, &pck_recv, pck_len, 0, &addr_recv, &addr_len);
            if (err == -1)
            {
                continue;
            }
            if (memcmp(&addr_recv, peer, addr_len) != 0 || pck_recv.magic != PEER_CONNEC_PEER_CHAT)
            {
                free(peer);
                continue;
            }
            printf("%s\n", (char *)&pck_recv.addr);
            free(peer);
        }
    }
    return 0;
}
void log_magic_pck(const struct MagicPck *pck)
{
    struct sockaddr_in *p = (struct sockaddr_in *)&(pck->addr);
    SLOG("%s:%d", inet_ntoa(p->sin_addr), ntohs(p->sin_port));
}